#include <winsock2.h>
#include <stdio.h>
#include <windows.h>

#pragma comment(lib, "ws2_32.lib")

/*

Copyright (c)  2001-2007 xiaozhi

Module name :

	ping.c
	
Abstract :	
	
	ICMP ping implement .

Author :
	
	xiaozhi

Date:

	2007/3/21   21:36

Modify :

*/

typedef struct Icmp
{
	unsigned char	icmp_type;	/* message type */
	unsigned char	icmp_code;	/* code		*/
	unsigned char	icmp_checksum;	/* check sum	*/

	unsigned char	icmp_id;	/* ID with use single sign request, usually set Process ID */
	unsigned char	icmp_sequence;	/* sequence number */
	unsigned char	icmp_timestamp;	/* time stamp	*/	
}icmp, *picmp;

USHORT checksum(USHORT *buff, int size)
{
	unsigned long cksum  = 0;

	while(size > 1)
	{
		cksum += *buff++;
		size -= sizeof(USHORT);
	}

	if(size)
	{
		cksum += *(UCHAR*)buff;
	}

	cksum = (cksum >> 16) + (cksum & 0xffff);
	cksum += (cksum >> 16);

	return (USHORT)(~cksum);
}

int main()
{
	char		szdestip[] = "127.0.0.1";
	SOCKET		sraw;
	SOCKADDR_IN	dest, from;
	char		buff[sizeof(icmp) + 32];
	USHORT		nseq	= 0;
	char		recvbuf[1024];
	int		nlen	= sizeof(from);
	static int	ncount	= 0;
	int		nret;
	int		ntick;
	imcp		*picmp, *precvicmp;

	if (WSAStartup(MAKEWORD(2,2), &wsd) != 0)
	{
		printf("Failed to load Winsock!\n");
		return 1;
	} 

	sraw = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);

	SetTimeout(sraw, 1000, TRUE);

	dest.sin_family = AF_INET;
	dest.sin_port	= htons(0);
	dest.sin_addr.S_un.S_addr = inet_addr(szdestip);

	picmp->icmp_type = 8;
	picmp->icmp_code = 0;
	picmp->icmp_id	 = (USHORT)GetCurrentProcessId();
	picmp->icmp_checksum = 0;
	picmp->icmp_sequence = 0;

	memset(&buff[sizeof(icmp)], 'E', 32);

	while(1)
	{
		if(ncount++ == 4)
			break;

		picmp->icmp_checksum	= 0;
		picmp->icmp_timestamp	= GetTickCount();
		picmp->icmp_sequence	= nseq++;
		picmp->icmp_checksum	= checksum((USHORT *)buff, sizeof(icmp) + 32);
		nret = sendto(sraw, buff, sizeof(icmp) + 32, 0, (SOCKADDR *)&dest, sizeof(dest));
		
		if(nret == SOCKET_ERROR)
		{
			printf("sendto() failed : %d\n", WSAGetLastError());
			return -1;
		}

		nret = recvfrom(sraw, recvbuf, 1024, 0, (sockaddr*)&from, &nlen);

		if(nret == SOCKET_ERROR)
		{
			if(WSAGetLastError() == WSAETIMEDOUT)
			{
				printf("time out \n");
				continue;
			}

			printf("recvfrom() failed ; %d\n", WSAGetLastError());
			return -1;
		}

		ntick = GetTickCount();

		if(nret < sizeof(IPHeader) + sizeof(icmp))
		{
			printf("Too few bytes from %s\n", inet_ntoa(from.sin_addr));
		}

		precvicmp = (icmp*)(recvbuf + sizeof(IPHeader));

		if(precvicmp->icmp_type != 0)
		{
			printf("nonecho type %d recvd \n", precvicmp->icmp_type);
			return -1;
		}

		if(precvicmp_icmp_id != GetCurrentProcessId())
		{
			printf("someone else's packet ! \n");
			return -1;
		}

		printf("%d bytes from %s : ", nret, inet_ntoa(from.sin_addr));
		printf("icmp_seq = %d", precvicmp->icmp_sequence);
		printf("time : %d ms ", ntick - precvicmp->icmp_timestamp);
		printf("\n");

		Sleep(1000);
	}

	return 0;
}